#include <errno.h>
#include <stdlib.h>
#include <cstring>
#include <memory>
#include <initializer_list>
#include <iostream>

#include "node_api.h"
#include "common.h"
#include "napi_util.h"
#include "base_context.h"
#include "http_object.h"
#include "request_method.h"
#include "response_code.h"

namespace NodeApi {
static napi_value CreateHttp(napi_env env, napi_callback_info info)
{
    napi_value result = nullptr;
    napi_value constructor = nullptr;
    napi_get_reference_value(env, HttpObject::g_httpRef, &constructor);
    napi_new_instance(env, constructor, 0, nullptr, &result);
    return result;
}

napi_value InitHttpRequest(napi_env env, napi_value exports)
{
    napi_property_descriptor properties[] = {DECLARE_NAPI_FUNCTION("createHttp", CreateHttp)};
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(properties) / sizeof(*properties), properties));
    return exports;
}

napi_value InitRequestMethod(napi_env env, napi_value exports)
{
    napi_value object = nullptr;
    napi_create_object(env, &object);
    NapiUtil::SetPropertyStringUtf8(env, object, RequestMethod::GET, RequestMethod::GET);
    NapiUtil::SetPropertyStringUtf8(env, object, RequestMethod::HEAD, RequestMethod::HEAD);
    NapiUtil::SetPropertyStringUtf8(env, object, RequestMethod::POST, RequestMethod::POST);
    NapiUtil::SetPropertyStringUtf8(env, object, RequestMethod::DELETE, RequestMethod::DELETE);
    NapiUtil::SetPropertyStringUtf8(env, object, RequestMethod::TRACE, RequestMethod::TRACE);
    NapiUtil::SetPropertyStringUtf8(env, object, RequestMethod::OPTIONS, RequestMethod::OPTIONS);
    NapiUtil::SetPropertyStringUtf8(env, object, RequestMethod::CONNECT, RequestMethod::CONNECT);
    napi_property_descriptor exportFuncs[] = {DECLARE_NAPI_PROPERTY("RequestMethod", object)};
    napi_define_properties(env, exports, sizeof(exportFuncs) / sizeof(*exportFuncs), exportFuncs);
    return exports;
}

napi_value InitResponseCode(napi_env env, napi_value exports)
{
    napi_value object = nullptr;
    napi_create_object(env, &object);
    NapiUtil::SetPropertyInt32(env, object, "OK", ResponseCode::OK);
    NapiUtil::SetPropertyInt32(env, object, "CREATED", ResponseCode::CREATED);
    NapiUtil::SetPropertyInt32(env, object, "ACCEPTED", ResponseCode::ACCEPTED);
    NapiUtil::SetPropertyInt32(env, object, "NOT_AUTHORITATIVE", ResponseCode::NOT_AUTHORITATIVE);
    NapiUtil::SetPropertyInt32(env, object, "NO_CONTENT", ResponseCode::NO_CONTENT);
    NapiUtil::SetPropertyInt32(env, object, "RESET", ResponseCode::RESET);
    NapiUtil::SetPropertyInt32(env, object, "PARTIAL", ResponseCode::PARTIAL);
    NapiUtil::SetPropertyInt32(env, object, "MULT_CHOICE", ResponseCode::MULT_CHOICE);
    NapiUtil::SetPropertyInt32(env, object, "MOVED_PERM", ResponseCode::MOVED_PERM);
    NapiUtil::SetPropertyInt32(env, object, "MOVED_TEMP", ResponseCode::MOVED_TEMP);
    NapiUtil::SetPropertyInt32(env, object, "SEE_OTHER", ResponseCode::SEE_OTHER);
    NapiUtil::SetPropertyInt32(env, object, "NOT_MODIFIED", ResponseCode::NOT_MODIFIED);
    NapiUtil::SetPropertyInt32(env, object, "USE_PROXY", ResponseCode::USE_PROXY);
    NapiUtil::SetPropertyInt32(env, object, "BAD_REQUEST", ResponseCode::BAD_REQUEST);
    NapiUtil::SetPropertyInt32(env, object, "UNAUTHORIZED", ResponseCode::UNAUTHORIZED);
    NapiUtil::SetPropertyInt32(env, object, "PAYMENT_REQUIRED", ResponseCode::PAYMENT_REQUIRED);
    NapiUtil::SetPropertyInt32(env, object, "FORBIDDEN", ResponseCode::FORBIDDEN);
    NapiUtil::SetPropertyInt32(env, object, "NOT_FOUND", ResponseCode::NOT_FOUND);
    NapiUtil::SetPropertyInt32(env, object, "BAD_METHOD", ResponseCode::BAD_METHOD);
    NapiUtil::SetPropertyInt32(env, object, "NOT_ACCEPTABLE", ResponseCode::NOT_ACCEPTABLE);
    NapiUtil::SetPropertyInt32(env, object, "PROXY_AUTH", ResponseCode::PROXY_AUTH);
    NapiUtil::SetPropertyInt32(env, object, "CLIENT_TIMEOUT", ResponseCode::CLIENT_TIMEOUT);
    NapiUtil::SetPropertyInt32(env, object, "CONFLICT", ResponseCode::CONFLICT);
    NapiUtil::SetPropertyInt32(env, object, "GONE", ResponseCode::GONE);
    NapiUtil::SetPropertyInt32(env, object, "LENGTH_REQUIRED", ResponseCode::LENGTH_REQUIRED);
    NapiUtil::SetPropertyInt32(env, object, "PRECON_FAILED", ResponseCode::PRECON_FAILED);
    NapiUtil::SetPropertyInt32(env, object, "ENTITY_TOO_LARGE", ResponseCode::ENTITY_TOO_LARGE);
    NapiUtil::SetPropertyInt32(env, object, "REQ_TOO_LONG", ResponseCode::REQ_TOO_LONG);
    NapiUtil::SetPropertyInt32(env, object, "UNSUPPORTED_TYPE", ResponseCode::UNSUPPORTED_TYPE);
    NapiUtil::SetPropertyInt32(env, object, "INTERNAL_ERROR", ResponseCode::INTERNAL_ERROR);
    NapiUtil::SetPropertyInt32(env, object, "NOT_IMPLEMENTED", ResponseCode::NOT_IMPLEMENTED);
    NapiUtil::SetPropertyInt32(env, object, "BAD_GATEWAY", ResponseCode::BAD_GATEWAY);
    NapiUtil::SetPropertyInt32(env, object, "UNAVAILABLE", ResponseCode::UNAVAILABLE);
    NapiUtil::SetPropertyInt32(env, object, "GATEWAY_TIMEOUT", ResponseCode::GATEWAY_TIMEOUT);
    NapiUtil::SetPropertyInt32(env, object, "VERSION", ResponseCode::VERSION);
    napi_property_descriptor exportEnums[] = {DECLARE_NAPI_PROPERTY("RequestMethod", object)};
    napi_define_properties(env, exports, sizeof(exportEnums) / sizeof(*exportEnums), exportEnums);
    return exports;
}

napi_value Init(napi_env env, napi_value exports)
{
    HttpObject::InitHttp(env, exports);
    InitRequestMethod(env, exports);
    InitResponseCode(env, exports);
    InitHttpRequest(env, exports);
    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
} // namespace NodeApi